package com.tsq.intelligence.provider.service.impl;

import com.google.gson.JsonObject;
import com.tsq.intelligence.api.dto.*;
import com.tsq.intelligence.api.vo.*;
import com.tsq.intelligence.provider.config.Config;
import com.tsq.intelligence.provider.service.ITextService;
import com.tsq.intelligence.provider.utils.Constant;
import com.tsq.intelligence.provider.utils.EncryptUtil;
import com.tsq.intelligence.provider.utils.HttpUtil;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

import static org.apache.commons.codec.binary.Base64.encodeBase64;

@Service
@Transactional(rollbackFor = Exception.class)
public class TextServiceImpl implements ITextService {

    @Autowired
    private Config config;

    /**
     * 公共组装http请求头
     */
    private Map<String, String> buildHttpHeader(String param) throws UnsupportedEncodingException {
        // 时间戳
        String curTime = System.currentTimeMillis() / 1000L + "";
        // 接口密钥
        String apiKey = config.getTextApiKey();
        // 讯飞开放平台应用ID
        String appid = config.getAppId();
        // 生成令牌
        String checkSum = DigestUtils.md5Hex(apiKey + curTime + param);
        // 组装请求头
        Map<String, String> header = new HashMap<String, String>();
        header.put("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
        header.put("X-Param", param);
        header.put("X-CurTime", curTime);
        header.put("X-CheckSum", checkSum);
        header.put("X-Appid", appid);
        return header;
    }

    /**
     * 手写文字识别 组装http请求头
     *
     * @param handWritingDTO
     * @return
     */
    private Map<String, String> buildHandWriteHeader(HandWritingDTO handWritingDTO) throws UnsupportedEncodingException {
        String language = handWritingDTO.getLanguage();
        String location = handWritingDTO.getLocation();
        String imei = handWritingDTO.getImei();
        String osid = handWritingDTO.getOsid();
        String ua = handWritingDTO.getUa();
        // 业务参数
        String param = "{\"language\":\"" + language + "\"" + ",\"location\":\"" + location + "\"" +
                ",\"imei\":\"" + imei + "\"" + ",\"osid\":\"" + osid + "\"" + ",\"ua\":\"" + ua + "\"}";
        String X_Param = new String(encodeBase64(param.getBytes("UTF-8")));
        Map<String, String> header = buildHttpHeader(X_Param);
        return header;
    }

    /**
     * 手写文字识别
     */
    @Override
    public HandWritingVO transferImageToText(HandWritingDTO handWritingDTO, MultipartFile file) throws IOException, ParseException {
        Map<String, String> header = buildHandWriteHeader(handWritingDTO);
        // 读取图像文件，转二进制数组，然后Base64编码
        byte[] imageByteArray = file.getBytes();
        String imageBase64 = new String(encodeBase64(imageByteArray), "UTF-8");
        String bodyParam = "image=" + imageBase64;
        String result = HttpUtil.doPost(Constant.HAND_WRITING_URL, header, bodyParam);
        HandWritingVO handWriting = HandWritingVO.retData(result);
        return handWriting;
    }

    /**
     * 印刷文字识别 组装http请求头
     *
     * @param handWritingDTO
     * @return
     */
    private Map<String, String> buildGeneralHeader(HandWritingDTO handWritingDTO) throws UnsupportedEncodingException {
        String language = handWritingDTO.getLanguage();
        String location = handWritingDTO.getLocation();
        // 业务参数
        String param = "{\"location\":\"" + location + "\",\"language\":\"" + language + "\"}";
        String X_Param = new String(encodeBase64(param.getBytes("UTF-8")));
        Map<String, String> header = buildHttpHeader(X_Param);
        return header;
    }

    /**
     * 印刷体文字识别
     *
     * @param handWritingDTO file
     * @throws IOException
     */
    @Override
    public PrintWordVO transferPrintWordToText(HandWritingDTO handWritingDTO,MultipartFile file) throws IOException {
        Map<String, String> header = buildGeneralHeader(handWritingDTO);
        byte[] imageByteArray = file.getBytes();
        String imageBase64 = new String(encodeBase64(imageByteArray), "UTF-8");
        String result = HttpUtil.doPost(Constant.GENERAL_URL, header, "image=" + URLEncoder.encode(imageBase64, "UTF-8"));
        PrintWordVO printWord = PrintWordVO.retData(result);
        return printWord;
    }

    /**
     * 多语种印刷文字识别 组装http请求头
     *
     * @param handWritingDTO
     * @return
     */
    private Map<String, String> buildMoreGeneralHeader(HandWritingDTO handWritingDTO) throws UnsupportedEncodingException {
        String engine_type = Constant.RECOGNIZE_DOCUNMENT;
        String imei = handWritingDTO.getImei();
        String osid = handWritingDTO.getOsid();
        String ua = handWritingDTO.getUa();
        // 业务参数
        String param = "{\"engine_type\":\"" + engine_type + "\"" +
                ",\"imei\":\"" + imei + "\"" + ",\"osid\":\"" + osid + "\"" + ",\"ua\":\"" + ua + "\"}";
        String X_Param = new String(encodeBase64(param.getBytes("UTF-8")));
        Map<String, String> header = buildHttpHeader(X_Param);
        return header;
    }

    /**
     * 多语种印刷体文字识别
     *
     * @param handWritingDTO  file
     * @throws IOException
     */
    @Override
    public MorePrintWordVO transferMorePrintWordToText(HandWritingDTO handWritingDTO ,MultipartFile file) throws IOException {
        Map<String, String> header = buildMoreGeneralHeader(handWritingDTO);
        byte[] imageByteArray = file.getBytes();
        String imageBase64 = new String(encodeBase64(imageByteArray), "UTF-8");
        String result = HttpUtil.doPost(Constant.MORE_GENERAL_URL, header, "image=" + URLEncoder.encode(imageBase64, "UTF-8"));
        MorePrintWordVO morePrintWord = MorePrintWordVO.retData(result);
        return morePrintWord;
    }

    /**
     * 名片识别 组装http请求头
     *
     * @param handWritingDTO
     * @return
     */
    private Map<String, String> buildBusinessCardHeader(HandWritingDTO handWritingDTO) throws UnsupportedEncodingException {
        String engineType = Constant.BUSINESS_CARD;
        String imei = handWritingDTO.getImei();
        String osid = handWritingDTO.getOsid();
        String ua = handWritingDTO.getUa();
        // 业务参数
        String param = "{\"engine_type\":\"" + engineType + "\"" +
                ",\"imei\":\"" + imei + "\"" + ",\"osid\":\"" + osid + "\"" + ",\"ua\":\"" + ua + "\"}";
        String X_Param = new String(encodeBase64(param.getBytes("UTF-8")));
        Map<String, String> header = buildHttpHeader(X_Param);
        return header;
    }

    /**
     * 名片识别
     * @param handWritingDTO
     * @throws IOException
     */
    @Override
    public BusinessCardVO transferCardToText(HandWritingDTO handWritingDTO ,MultipartFile file) throws IOException {
        Map<String, String> header = buildBusinessCardHeader(handWritingDTO);
        byte[] imageByteArray = file.getBytes();
        String imageBase64 = new String(encodeBase64(imageByteArray), "UTF-8");
        String result = HttpUtil.doPost(Constant.BUSINESS_CARD_URL, header, "image=" + URLEncoder.encode(imageBase64, "UTF-8"));
        BusinessCardVO businessCard = BusinessCardVO.retData(result);
        return businessCard;
    }

    /**
     * 组装http请求头
     */
    private Map<String, String> buildIdCardHeader(IdCardDTO idCardDTO) throws UnsupportedEncodingException {
        String engineType = Constant.ID_CARD;
        String headPortrait = idCardDTO.getHeadPortrait();
        String cropImage = idCardDTO.getCropImage();
        String idNumberImage = idCardDTO.getIdNumberImage();
        String recognizeMode = idCardDTO.getRecognizeMode();
        // 业务参数
        String param = "{\"engine_type\":\"" + engineType + "\",\"head_portrait\":\"" + headPortrait + "\",\"id_number_image\":\"" + idNumberImage +"\",\"crop_image\":\"" + cropImage +"\",\"recognize_mode\":\"" + recognizeMode +"\"}";
        String X_Param = new String(encodeBase64(param.getBytes("UTF-8")));
        Map<String, String> header = buildHttpHeader(X_Param);
        return header;
    }

    /**
     * 身份证识别
     * @param idCardDTO   file
     * @throws IOException
     */
    @Override
    public IdCardVO transferIdCardToText(IdCardDTO idCardDTO , MultipartFile file) throws IOException {
        Map<String, String> header = buildIdCardHeader(idCardDTO);
        byte[] imageByteArray = file.getBytes();
        String imageBase64 = new String(encodeBase64(imageByteArray), "UTF-8");
        String result = HttpUtil.doPost(Constant.ID_CARD_URL, header, "image=" + URLEncoder.encode(imageBase64, "UTF-8"));
        IdCardVO idCard = IdCardVO.retData(result);
        return idCard;
    }


    /**
     * 银行卡识别
     * @param bankCardDTO  file
     * @throws IOException
     */
    @Override
    public BankCardVO transferBankCardToText(BankCardDTO bankCardDTO, MultipartFile file) throws IOException {
        Map<String, String> header = buildBankCardHeader(bankCardDTO);
        byte[] imageByteArray = file.getBytes();
        String imageBase64 = new String(encodeBase64(imageByteArray), "UTF-8");
        String result = HttpUtil.doPost(Constant.BANK_CARD_URL, header, "image=" + URLEncoder.encode(imageBase64, "UTF-8"));
        BankCardVO bankCard = BankCardVO.retData(result);
        return bankCard;
    }

    /**
     * 组装http请求头
     */
    private  Map<String, String> buildBankCardHeader(BankCardDTO bankCardDTO) throws UnsupportedEncodingException {
        String engineType = Constant.BANK_CARD;
        String cardNumberImage = bankCardDTO.getCardNumberImage();
        String imei = bankCardDTO.getImei();
        String osid = bankCardDTO.getOsid();
        String ua = bankCardDTO.getUa();
        // 业务参数
        String param = "{\"engine_type\":\"" + engineType + "\"" +
                ",\"imei\":\"" + imei + "\"" + ",\"osid\":\"" + osid + "\"" + ",\"ua\":\"" + ua + "\"" + ",\"cardNumber_image\":\"" + cardNumberImage + "\"}";
        String X_Param = new String(encodeBase64(param.getBytes("UTF-8")));
        Map<String, String> header = buildHttpHeader(X_Param);
        return header;
    }

    /**
     * 营业执照识别
     * @param commonDTO  file
     * @throws IOException
     */
    @Override
    public BusinessLicenseVO transferLicenseToText(CommonDTO commonDTO, MultipartFile file) throws IOException {
        Map<String, String> header = buildLicenseHeader(commonDTO);
        byte[] imageByteArray = file.getBytes();
        String imageBase64 = new String(encodeBase64(imageByteArray), "UTF-8");
        String result = HttpUtil.doPost(Constant.LICENSE_URL, header, "image=" + URLEncoder.encode(imageBase64, "UTF-8"));
        BusinessLicenseVO businessLicense = BusinessLicenseVO.retData(result);
        return businessLicense;
    }

    /**
     * 组装http请求头
     */
    private Map<String, String> buildLicenseHeader(CommonDTO commonDTO) throws UnsupportedEncodingException {
        String engineType = Constant.BUSINESS_LICENSE;
        String imei = commonDTO.getImei();
        String osid = commonDTO.getOsid();
        String ua = commonDTO.getUa();
        // 业务参数
        String param = "{\"engine_type\":\"" + engineType + "\"" +
                ",\"imei\":\"" + imei + "\"" + ",\"osid\":\"" + osid + "\"" + ",\"ua\":\"" + ua + "\"}";
        String X_Param = new String(encodeBase64(param.getBytes("UTF-8")));
        Map<String, String> header = buildHttpHeader(X_Param);
        return header;
    }

    /**
     * 增值税发票识别
     * @param file
     * @throws IOException
     */
    @Override
    public InvoiceVO transferInvoiceToText(MultipartFile file) throws IOException {
        Map<String, String> header = buildInvoiceHeader();
        byte[] imageByteArray = file.getBytes();
        String imageBase64 = new String(encodeBase64(imageByteArray), "UTF-8");
        String result = HttpUtil.doPost(Constant.INVOICE_URL, header, "image=" + URLEncoder.encode(imageBase64, "UTF-8"));
        InvoiceVO invoice =  InvoiceVO.retData(result);
        return invoice;
    }

    /**
     * 组装http请求头
     */
    private Map<String, String> buildInvoiceHeader() throws UnsupportedEncodingException {
        String engineType = Constant.INVOICE;
        // 业务参数
        String param = "{\"engine_type\":\"" + engineType + "\"}";
        String X_Param = new String(encodeBase64(param.getBytes("UTF-8")));
        Map<String, String> header = buildHttpHeader(X_Param);
        return header;
    }

    /**
     * 拍照速算
     * @param
     * @throws Exception
     */
    @Override
    public TakePictureToCalculateVO takePictureToCalculate(MultipartFile file) throws Exception {
        Map <String,String> map= new HashMap<String,String>();
        map.put("ent","math-arith");
        String body = buildHttpBody(map,file);
        Map<String, String> header = buildItrHeader(body,Constant.WEBITR_URL);
        String retStr = HttpUtil.doPost(Constant.WEBITR_URL, header, body);
        TakePictureToCalculateVO takePictureToCalculateVO = TakePictureToCalculateVO.retData(retStr);
        return takePictureToCalculateVO;
    }

    /**
     * 公式识别
     * @param
     * @throws Exception
     */
    @Override
    public FormulaToCalculateVO formulaToCalculate(MultipartFile file) throws Exception {
        Map <String,String> map= new HashMap<String,String>();
        map.put("ent","teach-photo-print");
        String body = buildHttpBody(map,file);
        Map<String, String> header = buildItrHeader(body,Constant.WEBITR_URL);
        String retStr = HttpUtil.doPost(Constant.WEBITR_URL, header, body);
        FormulaToCalculateVO formulaToCalculateVO= FormulaToCalculateVO.retData(retStr);
        return formulaToCalculateVO;
    }

    /**
     * 指尖文字识别
     * @param
     * @throws Exception
     */
    @Override
    public FingerToTextVO fingertipOfImageToText(FingerDTO fingerDTO, MultipartFile file) throws Exception {
        String resultStr = "";
        String body = buildFingerBody(fingerDTO,file);
        Map<String, String> header = buildItrHeader(body,Constant.FINGERTIP_URL);
        resultStr =  HttpUtil.doPost(Constant.FINGERTIP_URL, header, body);
        FingerToTextVO fingerToText = FingerToTextVO.retData(resultStr);
        return fingerToText;
    }

    /**
     *   组装http请求头
     */
    private Map<String, String> buildItrHeader(String body,String httpUrl) throws Exception {
        Map<String, String> header = new HashMap<String, String>();
        URL url = new URL(httpUrl);
        //时间戳
        SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
        format.setTimeZone(TimeZone.getTimeZone("GMT"));
        Date dateD = new Date();
        String date = format.format(dateD);
        //对body进行sha256签名,生成digest头部，POST请求必须对body验证
        String digestBase64 = "SHA-256=" + EncryptUtil.signBody(body);
        //hmacsha256加密原始字符串
        StringBuilder builder = new StringBuilder("host: ").append(url.getHost()).append("\n").//
                append("date: ").append(date).append("\n").//
                append("POST ").append(url.getPath()).append(" HTTP/1.1").append("\n").//
                append("digest: ").append(digestBase64);
        String sha = EncryptUtil.hmacSign(builder.toString(), config.getCalculateApiSecret());
        //组装authorization
        String authorization = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", config.getCalculateApiKey(), "hmac-sha256", "host date request-line digest", sha);
        header.put("Authorization", authorization);
        header.put("Content-Type", "application/json");
        header.put("Accept", "application/json,version=1.0");
        header.put("Host", url.getHost());
        header.put("Date", date);
        header.put("Digest", digestBase64);
        return header;
    }

    /**
     * 组装http请求体
     */
    private String buildHttpBody(Map<String, String> map,MultipartFile file) throws Exception {
        JsonObject body = new JsonObject();
        JsonObject business = new JsonObject();
        JsonObject common = new JsonObject();
        JsonObject data = new JsonObject();
        //填充common
        common.addProperty("app_id", config.getAppId());
        //填充business
        String ent = map.get("ent");
        business.addProperty("ent", ent);
        business.addProperty("aue", "raw");
        //填充data
        byte[] imageByteArray = file.getBytes();
        String imageBase64 = new String(Base64.getEncoder().encodeToString(imageByteArray));
        data.addProperty("image", imageBase64);
        //填充body
        body.add("common", common);
        body.add("business", business);
        body.add("data", data);
        return body.toString();
    }

    /**
     * 组装http请求体
     */
    private String buildFingerBody(FingerDTO fingerDTO, MultipartFile file) throws Exception {
        JsonObject body = new JsonObject();
        JsonObject business = new JsonObject();
        JsonObject common = new JsonObject();
        JsonObject data = new JsonObject();
        //填充common
        common.addProperty("app_id", config.getAppId());
        //填充business
        business.addProperty("ent", "fingerocr");
        business.addProperty("method", "dynamic");
        business.addProperty("mode", fingerDTO.getMode());
        /*business.addProperty("cut_w_scale", fingerDTO.getCutWScale());
        business.addProperty("cut_h_scale", fingerDTO.getCutHScale());
        business.addProperty("cut_shift", fingerDTO.getCutShift());
        business.addProperty("resize_w", fingerDTO.getResizeW());
        business.addProperty("resize_h", fingerDTO.getResizeH());*/
        //填充data
        byte[] imageByteArray = file.getBytes();
        String imageBase64 = new String(Base64.getEncoder().encodeToString(imageByteArray));
        data.addProperty("image", imageBase64);
        //填充body
        body.add("common", common);
        body.add("business", business);
        body.add("data", data);
        return body.toString();
    }

}
